package com.example.redis.demo.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.redis.demo.dto.UserGeoDTO;
import com.example.redis.demo.exception.BusinessException;
import com.example.redis.demo.mapper.UserLoginLogMapper;
import com.example.redis.demo.mapper.UserMapper;
import com.example.redis.demo.model.User;
import com.example.redis.demo.model.UserLoginLog;
import com.example.redis.demo.response.GetMyNearByResponse;
import com.example.redis.demo.service.UserLoginLogService;
import io.jsonwebtoken.lang.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
public class UserLoginLogServiceImpl
        extends ServiceImpl<UserLoginLogMapper, UserLoginLog>
        implements UserLoginLogService {


    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Override
    public GetMyNearByResponse getByUserIdAndDistance(Long userId, Long m) {
        String geoKey = "u:login:geo";
        GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new QueryWrapper<User>().lambda();
        userLambdaQueryWrapper.eq(User::getId, userId);
        User user = this.userMapper.selectOne(userLambdaQueryWrapper);
        if (user == null) {
            throw BusinessException.error("用户不存在");
        }
        List<GetMyNearByResponse.NearByUser> data = new ArrayList<>();
        final UserGeoDTO userGeoDTO = new UserGeoDTO();
        userGeoDTO.setUserName(user.getUsername());
        userGeoDTO.setUserId(user.getId());
        GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResults = geoOperations.geoRadiusByMember(geoKey,
                userGeoDTO.getUserId() + ":" + userGeoDTO.getUserName(),
                new Distance(m), RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                        .includeCoordinates().includeDistance());
        geoResults.getContent().forEach(
                it -> {
                    final GetMyNearByResponse.NearByUser item = new GetMyNearByResponse.NearByUser();
                    Distance distance = it.getDistance();
                    item.setDistanceM(distance.getValue());
                    Point point = it.getContent().getPoint();
                    item.setLatitude(point.getY());
                    item.setLongitude(point.getX());
                    //暂时不处理
                    String userIdAndName = it.getContent().getName().toString();
                    List<String> userInfo = Arrays.asList(
                            Strings.split(userIdAndName, ":"));
                    item.setUserId(Long.valueOf(userInfo.get(0)));
                    item.setUserName(userInfo.get(1));
                    data.add(item);
                }
        );
        return new GetMyNearByResponse("200", "success", data);
    }


    /**
     * 获取附近最近的人
     *
     * @param userId
     * @param m
     * @return
     */
    @Override
    public GetMyNearByResponse.NearByUser nearestMe(Long userId, Long m) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new QueryWrapper<User>().lambda();
        userLambdaQueryWrapper.eq(User::getId, userId);
        User user = this.userMapper.selectOne(userLambdaQueryWrapper);
        if (user == null) {
            throw BusinessException.error("用户不存在");
        }
        String geoKey = "u:login:geo";
        GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();
        final UserGeoDTO geoDTO = new UserGeoDTO();
        geoDTO.setUserId(user.getId());
        geoDTO.setUserName(user.getUsername());
        GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResults = geoOperations.geoRadiusByMember(geoKey, geoDTO.getUserId() + ":" + geoDTO.getUserName(),
                new Distance(m),
                RedisGeoCommands.GeoRadiusCommandArgs
                        .newGeoRadiusArgs()
                        .includeDistance()
                        .includeCoordinates()
                        .sortAscending().limit(2));
        if (CollectionUtil.isNotEmpty(geoResults.getContent()) && geoResults.getContent().size() > 1) {
            GetMyNearByResponse.NearByUser nearByUser = new GetMyNearByResponse.NearByUser();
            GeoResult<RedisGeoCommands.GeoLocation<Object>> geoLocation = geoResults.getContent().get(1);
            Distance distance = geoLocation.getDistance();
            if (distance != null) {
                nearByUser.setDistanceM(distance.getValue());
            }
            RedisGeoCommands.GeoLocation<Object> value = geoLocation.getContent();
            if (value != null) {
                String name = value.getName().toString();
                List<String> names = Arrays.asList(Strings.split(name, ":"));
                nearByUser.setUserName(names.size() > 0 ? names.get(1) : null);
                nearByUser.setUserId(Long.valueOf(names.get(0)));
                Point point = value.getPoint();
                if (point != null) {
                    nearByUser.setLongitude(value.getPoint().getX());
                    nearByUser.setLatitude(value.getPoint().getY());
                }
            }
            return nearByUser;
        }
        return null;
    }


    /**
     * 获取最远的人
     *
     * @param userId
     * @param m
     * @return
     */
    @Override
    public GetMyNearByResponse.NearByUser longestMe(Long userId, Long m) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new QueryWrapper<User>().lambda();
        userLambdaQueryWrapper.eq(User::getId, userId);
        User user = this.userMapper.selectOne(userLambdaQueryWrapper);
        if (user == null) {
            throw BusinessException.error("用户不存在");
        }
        String geoKey = "u:login:geo";
        GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();
        final UserGeoDTO geoDTO = new UserGeoDTO();
        geoDTO.setUserId(user.getId());
        geoDTO.setUserName(user.getUsername());
        GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResults = geoOperations.geoRadiusByMember(geoKey, geoDTO.getUserId() + ":" + geoDTO.getUserName(),
                new Distance(m),
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates()
                        .sortDescending().limit(1));
        if (CollectionUtil.isNotEmpty(geoResults.getContent())) {
            GetMyNearByResponse.NearByUser nearByUser = new GetMyNearByResponse.NearByUser();
            GeoResult<RedisGeoCommands.GeoLocation<Object>> geoLocation = geoResults.getContent().get(0);
            Distance distance = geoLocation.getDistance();
            if (distance != null) {
                nearByUser.setDistanceM(distance.getValue());
            }
            RedisGeoCommands.GeoLocation<Object> value = geoLocation.getContent();
            if (value != null) {
                String name = value.getName().toString();
                List<String> names = Arrays.asList(Strings.split(name, ":"));
                nearByUser.setUserName(names.size() > 0 ? names.get(1) : null);
                nearByUser.setUserId(Long.valueOf(names.get(0)));
                Point point = value.getPoint();
                if (point != null) {
                    nearByUser.setLongitude(value.getPoint().getX());
                    nearByUser.setLatitude(value.getPoint().getY());
                }
            }
            return nearByUser;
        }
        return null;
    }
}
